/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.core; import java.util.*; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeSupport; import java.lang.ref.*; import org.openide.TopManager; import org.openide.filesystems.FileSystem; import org.openide.filesystems.*; import org.openide.loaders.DataFilter; import org.openide.loaders.DataFolder; import org.openide.loaders.DataObject; import org.openide.loaders.DataObjectNotFoundException; import org.openide.loaders.DataLoaderPool; import org.openide.nodes.FilterNode; import org.openide.nodes.AbstractNode; import org.openide.nodes.Node; import org.openide.nodes.Children; import org.openide.nodes.Children.SortedMap; import org.openide.nodes.NodeAdapter; import org.openide.util.WeakListener; import org.openide.util.RequestProcessor; import org.openide.util.enum.AlterEnumeration; import org.openide.util.enum.EmptyEnumeration; import org.openide.util.enum.SingletonEnumeration; import org.openide.util.enum.SequenceEnumeration; /** * Produces list of packages. * * @author Jan Jancura, Jaroslav Tulach */ final class Packages extends Object implements RepositoryListener, Comparator, FileChangeListener, PropertyChangeListener { /** values for notification of changes when parsing large number * of packages */ private static final int MIN_LIMIT = 8; private static final int MUL_LIMIT = 2; /** priority for parsing */ private static final int PRIORITY_WRITE = 3; /** priority for reading results from the * queue */ // is bigger to go before parsing private static final int PRIORITY_READ = 4; /** static instance */ private static Reference ref = new WeakReference (null); /** property names. list of all files, fired when list of all files * is changed */ public static final String PROP_LIST = "list"; // NOI18N /** property names. fired when a name of a file is changed. than the * new value contains the file object that chanded the name */ public static final String PROP_NAME = "name"; // NOI18N /** weak listener to changes in files */ private FileChangeListener weakFCL = WeakListener.fileChange (this, null); /** weak listener to changes in hidden property of file systems */ private PropertyChangeListener weakPCL = WeakListener.propertyChange ( this, null ); /** Set of all folders in the system. (FileObject) */ private TreeSet fileFolders; /** Supporting value for reorder of filesystems. Maps * (FileSystem, Integer) where int is the index of the filesystem * in repository * @associates Integer */ private Map indexOfFileSystem; /** request processor for processing of all packages. */ private RequestProcessor PROCESSOR = new RequestProcessor ( "All packages processor" // NOI18N ); /** property change support. */ private PropertyChangeSupport pcs = new PropertyChangeSupport (this); private Packages () { } /** When finalized, stop the request processor */ protected void finalize () { PROCESSOR.stop(); } /** Starts the initialization. */ private void initialize () { if (fileFolders == null) { synchronized (this) { if (fileFolders == null) { fileFolders = new TreeSet (this); Repository rep = TopManager.getDefault ().getRepository (); rep.addRepositoryListener (WeakListener.repository (this, rep)); // read all packages in the repository putPackages (null); // attach listener to hidden property change Enumeration en = rep.fileSystems (); while (en.hasMoreElements()) { FileSystem fs = (FileSystem)en.nextElement(); fs.addPropertyChangeListener (weakPCL); } } } } } // // The only public methods // /** Getter for default instance. */ public static Packages getDefault () { Packages p = (Packages)ref.get (); if (p == null) { synchronized (Packages.class) { if (p == null) { p = new Packages (); ref = new WeakReference (p); } } } return p; } public void addPropertyChangeListener (PropertyChangeListener pcl) { pcs.addPropertyChangeListener (pcl); } public void removePropertyChangeListener (PropertyChangeListener pcl) { pcs.removePropertyChangeListener (pcl); } /** Called when a PackageChildren object wants to update * its state */ public void update (final PackageChildren ch) { initialize (); PROCESSOR.post(new Runnable () { public void run () { ch.updatePackages (fileFolders); } }, 0, PRIORITY_READ); } // // end of public methods // /** Method for sending working tasks into the queue. */ private void postTask (Runnable r) { PROCESSOR.post (r, 0, PRIORITY_WRITE); } // FileChangeListener support /** Fired when a new folder is created. This action can only be * listened to in folders containing the created folder up to the root of * file system. * * @param fe the event describing context where action has taken place */ public void fileFolderCreated(FileEvent fe) { putPackages (fe.getFile ()); } /** Fired when a new file is created. This action can only be * listened in folders containing the created file up to the root of * file system. * * @param fe the event describing context where action has taken place */ public void fileDataCreated(FileEvent fe) { } /** Fired when a file is changed. * @param fe the event describing context where action has taken place */ public void fileChanged(FileEvent fe) { } /** Fired when a file is deleted. * @param fe the event describing context where action has taken place */ public void fileDeleted(final FileEvent fe) { if (fe.getFile () != fe.getSource ()) return; postTask (new Runnable () { public void run () { fileFolders.remove (fe.getFile ()); pcs.firePropertyChange (PROP_LIST, null, null); } }); } /** Fired when a file is renamed. * @param fe the event describing context where action has taken place * and the original name and extension. */ public void fileRenamed (final FileRenameEvent fe) { if (fe.getFile () != fe.getSource ()) return; postTask (new Runnable () { public void run () { pcs.firePropertyChange (PROP_NAME, null, fe.getFile ()); } }); } /** Fired when a file attribute is changed. * @param fe the event describing context where action has taken place, * the name of attribute and the old and new values. */ public void fileAttributeChanged(FileAttributeEvent fe) { } // RepositoryListener support ......................................................... /** * Adds packages for given FS. */ public void fileSystemAdded (final RepositoryEvent ev) { FileSystem fs = ev.getFileSystem (); if (fs.isHidden ()) return; putPackages (fs.getRoot()); fs.addPropertyChangeListener(weakPCL); } /** * Removes packages of given FS. */ public void fileSystemRemoved (final RepositoryEvent ev) { FileSystem fs = ev.getFileSystem (); removeFileSystemPackages (fs); fs.addPropertyChangeListener(weakPCL); } /** * Does nothing. */ public void fileSystemPoolReordered (RepositoryReorderedEvent ev) { // task = reinitialize based on original fileFolders postTask (new Runnable () { public void run () { LinkedList ll = new LinkedList (fileFolders); // this changes the comparator indexOfFileSystem = null; // so we have to reinsert new values fileFolders.clear (); fileFolders.addAll (ll); } }); } public void propertyChange(final java.beans.PropertyChangeEvent p1) { if (FileSystem.PROP_HIDDEN.equals(p1.getPropertyName())) { FileSystem fs = (FileSystem)p1.getSource(); if (fs.isHidden()) { removeFileSystemPackages (fs); } else { putPackages (fs.getRoot()); } } } // main methods ...................................................................... /** Create enumeration of all subfiles under given file. * @param fo file object * @return enum of FileObjects */ private static Enumeration createSubFolders (FileObject fo) { return new SequenceEnumeration ( new SingletonEnumeration (fo), fo.getFolders (true) ); } /** Create enumeration of all folders on all file systems. * @return enum of FileObjects */ private static Enumeration createFolders () { return new SequenceEnumeration ( new AlterEnumeration ( TopManager.getDefault ().getRepository ().getFileSystems () ) { protected Object alter (Object o) { FileSystem fs = (FileSystem)o; if (fs.isHidden () || !fs.isValid ()) { return EmptyEnumeration.EMPTY; } return createSubFolders (fs.getRoot ()); } } ); } /** * Returns TreeMap of file => nodes contained in given FileObject. For null * returns map of all files. */ private void putPackages ( FileObject source ) { Enumeration en = source == null ? createFolders () : createSubFolders (source); postTask (processPackages (en, MIN_LIMIT)); } /** Processes packages in the enumeration. * @param en the enumeration of files to process * @param cnt max count of packages to process in this task */ private Runnable processPackages ( final Enumeration en, final int cnt ) { return new Runnable () { private int limit = cnt; public void run () { if (!en.hasMoreElements ()) return; int i = 0; while (en.hasMoreElements () && i++ < limit) { FileObject fo = (FileObject) en.nextElement (); fo.addFileChangeListener (weakFCL); fileFolders.add (fo); } pcs.firePropertyChange (PROP_LIST, null, null); // post this runnable again till the enumeration // is empty limit *= MUL_LIMIT; postTask (this); } }; } /** Removes all packages from given file system. */ private void removeFileSystemPackages (final FileSystem fs) { postTask (new Runnable () { public void run () { Iterator it = fileFolders.iterator (); while (it.hasNext()) { FileObject fo = (FileObject)it.next (); try { if (fo.getFileSystem ().equals (fs)) { // file is on deleted file system it.remove (); } } catch (FileStateInvalidException e) { // remove too it.remove (); } } pcs.firePropertyChange (PROP_LIST, null, null); } }); } /** Getter for index of file system. */ private int indexOf (FileSystem fs) { Map ifs = indexOfFileSystem; if (ifs == null) { synchronized (this) { ifs = indexOfFileSystem; if (ifs == null) { Repository rep = TopManager.getDefault ().getRepository (); FileSystem[] arr = rep.toArray (); ifs = indexOfFileSystem = new HashMap ((int) (arr.length * 1.3)); for (int i = 0; i < arr.length; i++) { indexOfFileSystem.put (arr[i], new Integer (i)); } } } } Integer i = (Integer)ifs.get (fs); return i == null ? -1 : i.intValue (); } /** Comparator for two FileObjects representing folders. */ public int compare (Object o1, Object o2) { try { FileObject fo1 = (FileObject)o1; FileObject fo2 = (FileObject)o2; FileSystem fs1 = fo1.getFileSystem (); FileSystem fs2 = fo2.getFileSystem (); if (fs1.equals (fs2)) { return fo1.getPackageName ('.').compareTo (fo2.getPackageName ('.')); } else { return indexOf (fs1) - indexOf (fs2); } } catch (FileStateInvalidException ee) { return 0; } } } /* * Log * 5 Gandalf 1.4 1/13/00 Jaroslav Tulach I18N * 4 Gandalf 1.3 1/9/00 Jaroslav Tulach Renaming works better. * 3 Gandalf 1.2 1/7/00 Jaroslav Tulach #5156 * 2 Gandalf 1.1 11/29/99 Jaroslav Tulach Updates while parsing. * 1 Gandalf 1.0 11/29/99 Jaroslav Tulach * $ */